/**
* <copyright>
* </copyright>
*
*
*/
package org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui;
/**
* A CodeCompletionHelper can be used to derive completion proposals for partial
* documents. It runs the parser generated by EMFText in a special mode (i.e., the
* rememberExpectedElements mode). Based on the elements that are expected by the
* parser for different regions in the document, valid proposals are computed.
*/
public class MtextCodeCompletionHelper {
private org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextAttributeValueProvider attributeValueProvider = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextAttributeValueProvider();
private org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextMetaInformation metaInformation = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextMetaInformation();
/**
* Computes a set of proposals for the given document assuming the cursor is at
* 'cursorOffset'. The proposals are derived using the meta information, i.e., the
* generated language plug-in.
*
* @param originalResource
* @param content the documents content
* @param cursorOffset
*
* @return
*/
public org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal[] computeCompletionProposals(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource originalResource, String content, int cursorOffset) {
org.eclipse.emf.ecore.resource.ResourceSet resourceSet = new org.eclipse.emf.ecore.resource.impl.ResourceSetImpl();
// the shadow resource needs the same URI because reference resolvers may use the
// URI to resolve external references
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource resource = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource) resourceSet.createResource(originalResource.getURI());
java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(content.getBytes());
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextMetaInformation metaInformation = resource.getMetaInformation();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextParser parser = metaInformation.createParser(inputStream, null);
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[] expectedElements = parseToExpectedElements(parser, resource, cursorOffset);
if (expectedElements == null) {
return new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal[0];
}
if (expectedElements.length == 0) {
return new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal[0];
}
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedAfterCursor = java.util.Arrays.asList(getElementsExpectedAt(expectedElements, cursorOffset));
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedBeforeCursor = java.util.Arrays.asList(getElementsExpectedAt(expectedElements, cursorOffset - 1));
setPrefixes(expectedAfterCursor, content, cursorOffset);
setPrefixes(expectedBeforeCursor, content, cursorOffset);
// First, we derive all possible proposals from the set of elements that are
// expected at the cursor position.
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> allProposals = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> rightProposals = deriveProposals(expectedAfterCursor, content, resource, cursorOffset);
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> leftProposals = deriveProposals(expectedBeforeCursor, content, resource, cursorOffset - 1);
removeKeywordsEndingBeforeIndex(leftProposals, cursorOffset);
// Second, the set of left proposals (i.e., the ones before the cursor) is checked
// for emptiness. If the set is empty, the right proposals (i.e., the ones after
// the cursor) are also considered. If the set is not empty, the right proposal
// are discarded, because it does not make sense to propose them until the element
// before the cursor was completed.
allProposals.addAll(leftProposals);
// Count the proposals before the cursor that match the prefix
int leftMatchingProposals = 0;
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal leftProposal : leftProposals) {
if (leftProposal.getMatchesPrefix()) {
leftMatchingProposals++;
}
}
if (leftMatchingProposals == 0) {
allProposals.addAll(rightProposals);
}
// Third, the proposals are sorted according to their relevance. Proposals that
// matched the prefix are preferred over ones that did not. Finally, proposals are
// sorted alphabetically.
final java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> sortedProposals = new java.util.ArrayList<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>(allProposals);
java.util.Collections.sort(sortedProposals);
org.eclipse.emf.ecore.EObject root = null;
if (!resource.getContents().isEmpty()) {
root = resource.getContents().get(0);
}
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal proposal : sortedProposals) {
proposal.setRoot(root);
}
return sortedProposals.toArray(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal[sortedProposals.size()]);
}
public org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[] parseToExpectedElements(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextParser parser, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource resource, int cursorOffset) {
final java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements = parser.parseToExpectedElements(null, resource, cursorOffset);
if (expectedElements == null) {
return new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[0];
}
removeDuplicateEntries(expectedElements);
removeInvalidEntriesAtEnd(expectedElements);
return expectedElements.toArray(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[expectedElements.size()]);
}
/**
* Removes all expected elements that refer to the same terminal and that start at
* the same position.
*/
protected void removeDuplicateEntries(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements) {
int size = expectedElements.size();
// We split the list of expected elements into buckets where each bucket contains
// the elements that start at the same position.
java.util.Map<Integer, java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal>> map = new java.util.LinkedHashMap<Integer, java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal>>();
for (int i = 0; i < size; i++) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtIndex = expectedElements.get(i);
int start1 = elementAtIndex.getStartExcludingHiddenTokens();
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> list = map.get(start1);
if (list == null) {
list = new java.util.ArrayList<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal>();
map.put(start1, list);
}
list.add(elementAtIndex);
}
// Then, we remove all duplicate elements from each bucket individually.
for (int position : map.keySet()) {
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> list = map.get(position);
removeDuplicateEntriesFromBucket(list);
}
// After removing all duplicates, we merge the buckets.
expectedElements.clear();
for (int position : map.keySet()) {
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> list = map.get(position);
expectedElements.addAll(list);
}
}
/**
* Removes all expected elements that refer to the same terminal. Attention: This
* method assumes that the given list of expected terminals contains only elements
* that start at the same position.
*/
protected void removeDuplicateEntriesFromBucket(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements) {
int size = expectedElements.size();
for (int i = 0; i < size - 1; i++) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtIndex = expectedElements.get(i);
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextExpectedElement terminal = elementAtIndex.getTerminal();
for (int j = i + 1; j < size;) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtNext = expectedElements.get(j);
if (terminal.equals(elementAtNext.getTerminal())) {
expectedElements.remove(j);
size--;
} else {
j++;
}
}
}
}
protected void removeInvalidEntriesAtEnd(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements) {
for (int i = 0; i < expectedElements.size() - 1;) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtIndex = expectedElements.get(i);
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtNext = expectedElements.get(i + 1);
// If the two expected elements have a different parent in the syntax definition,
// we must not discard the second element, because is probably stems from a parent
// rule.
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.grammar.MtextSyntaxElement symtaxElementOfThis = elementAtIndex.getTerminal().getSymtaxElement();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.grammar.MtextSyntaxElement symtaxElementOfNext = elementAtNext.getTerminal().getSymtaxElement();
boolean differentParent = symtaxElementOfNext.getParent() != symtaxElementOfThis.getParent();
boolean sameStartExcludingHiddenTokens = elementAtIndex.getStartExcludingHiddenTokens() == elementAtNext.getStartExcludingHiddenTokens();
boolean differentFollowSet = elementAtIndex.getFollowSetID() != elementAtNext.getFollowSetID();
if (sameStartExcludingHiddenTokens && differentFollowSet && !differentParent) {
expectedElements.remove(i + 1);
} else {
i++;
}
}
}
/**
* Removes all proposals for keywords that end before the given index.
*/
protected void removeKeywordsEndingBeforeIndex(java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> proposals, int index) {
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> toRemove = new java.util.ArrayList<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal proposal : proposals) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal = proposal.getExpectedTerminal();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextExpectedElement terminal = expectedTerminal.getTerminal();
if (terminal instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString csString = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString) terminal;
int startExcludingHiddenTokens = expectedTerminal.getStartExcludingHiddenTokens();
if (startExcludingHiddenTokens + csString.getValue().length() - 1 < index) {
toRemove.add(proposal);
}
}
}
proposals.removeAll(toRemove);
}
protected String findPrefix(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedAtCursor, String content, int cursorOffset) {
if (cursorOffset < 0) {
return "";
}
int end = 0;
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedElement : expectedElements) {
if (expectedElement == expectedAtCursor) {
final int start = expectedElement.getStartExcludingHiddenTokens();
if (start >= 0 && start < Integer.MAX_VALUE) {
end = start;
}
break;
}
}
end = Math.min(end, cursorOffset);
final String prefix = content.substring(end, Math.min(content.length(), cursorOffset));
return prefix;
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> deriveProposals(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements, String content, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource resource, int cursorOffset) {
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> resultSet = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedElement : expectedElements) {
resultSet.addAll(deriveProposals(expectedElement, content, resource, cursorOffset));
}
return resultSet;
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> deriveProposals(final org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, String content, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTextResource resource, int cursorOffset) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextExpectedElement expectedElement = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextExpectedElement) expectedTerminal.getTerminal();
if (expectedElement instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString csString = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString) expectedElement;
return handleKeyword(expectedTerminal, csString, expectedTerminal.getPrefix());
} else if (expectedElement instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedBooleanTerminal) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedBooleanTerminal expectedBooleanTerminal = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedBooleanTerminal) expectedElement;
return handleBooleanTerminal(expectedTerminal, expectedBooleanTerminal, expectedTerminal.getPrefix());
} else if (expectedElement instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedEnumerationTerminal) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedEnumerationTerminal expectedEnumerationTerminal = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedEnumerationTerminal) expectedElement;
return handleEnumerationTerminal(expectedTerminal, expectedEnumerationTerminal, expectedTerminal.getPrefix());
} else if (expectedElement instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedStructuralFeature) {
final org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedStructuralFeature expectedFeature = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedStructuralFeature) expectedElement;
final org.eclipse.emf.ecore.EStructuralFeature feature = expectedFeature.getFeature();
final org.eclipse.emf.ecore.EClassifier featureType = feature.getEType();
final org.eclipse.emf.ecore.EObject container = findCorrectContainer(expectedTerminal);
// Here it gets really crazy. We need to modify the model in a way that reflects
// the state the model would be in, if the expected terminal were present. After
// computing the corresponding completion proposals, the original state of the
// model is restored. This procedure is required, because different models can be
// required for different completion situations. This can be particularly observed
// when the user has not yet typed a character that starts an element to be
// completed.
final java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> proposals = new java.util.ArrayList<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
expectedTerminal.materialize(new Runnable() {
public void run() {
if (feature instanceof org.eclipse.emf.ecore.EReference) {
org.eclipse.emf.ecore.EReference reference = (org.eclipse.emf.ecore.EReference) feature;
if (featureType instanceof org.eclipse.emf.ecore.EClass) {
if (reference.isContainment()) {
// the FOLLOW set should contain only non-containment references
assert false;
} else {
proposals.addAll(handleNCReference(expectedTerminal, container, reference, expectedTerminal.getPrefix(), expectedFeature.getTokenName()));
}
}
} else if (feature instanceof org.eclipse.emf.ecore.EAttribute) {
org.eclipse.emf.ecore.EAttribute attribute = (org.eclipse.emf.ecore.EAttribute) feature;
if (featureType instanceof org.eclipse.emf.ecore.EEnum) {
org.eclipse.emf.ecore.EEnum enumType = (org.eclipse.emf.ecore.EEnum) featureType;
proposals.addAll(handleEnumAttribute(expectedTerminal, expectedFeature, enumType, expectedTerminal.getPrefix(), container));
} else {
// handle EAttributes (derive default value depending on the type of the
// attribute, figure out token resolver, and call deResolve())
proposals.addAll(handleAttribute(expectedTerminal, expectedFeature, container, attribute, expectedTerminal.getPrefix()));
}
} else {
// there should be no other subclass of EStructuralFeature
assert false;
}
}
});
// Return the proposals that were computed in the closure call.
return proposals;
} else {
// there should be no other class implementing IExpectedElement
assert false;
}
return java.util.Collections.emptyList();
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleEnumAttribute(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedStructuralFeature expectedFeature, org.eclipse.emf.ecore.EEnum enumType, String prefix, org.eclipse.emf.ecore.EObject container) {
java.util.Collection<org.eclipse.emf.ecore.EEnumLiteral> enumLiterals = enumType.getELiterals();
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> result = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
for (org.eclipse.emf.ecore.EEnumLiteral literal : enumLiterals) {
String unResolvedLiteral = literal.getLiteral();
// use token resolver to get de-resolved value of the literal
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolverFactory tokenResolverFactory = metaInformation.getTokenResolverFactory();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolver tokenResolver = tokenResolverFactory.createTokenResolver(expectedFeature.getTokenName());
String resolvedLiteral = tokenResolver.deResolve(unResolvedLiteral, expectedFeature.getFeature(), container);
boolean matchesPrefix = matches(resolvedLiteral, prefix);
result.add(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal(expectedTerminal, resolvedLiteral, prefix, matchesPrefix, expectedFeature.getFeature(), container));
}
return result;
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleNCReference(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EReference reference, String prefix, String tokenName) {
// proposals for non-containment references are derived by calling the reference
// resolver switch in fuzzy mode.
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextReferenceResolverSwitch resolverSwitch = metaInformation.getReferenceResolverSwitch();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolverFactory tokenResolverFactory = metaInformation.getTokenResolverFactory();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextReferenceResolveResult<org.eclipse.emf.ecore.EObject> result = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextReferenceResolveResult<org.eclipse.emf.ecore.EObject>(true);
resolverSwitch.resolveFuzzy(prefix, container, reference, 0, result);
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextReferenceMapping<org.eclipse.emf.ecore.EObject>> mappings = result.getMappings();
if (mappings != null) {
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> resultSet = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextReferenceMapping<org.eclipse.emf.ecore.EObject> mapping : mappings) {
org.eclipse.swt.graphics.Image image = null;
if (mapping instanceof org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextElementMapping<?>) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextElementMapping<?> elementMapping = (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextElementMapping<?>) mapping;
Object target = elementMapping.getTargetElement();
// de-resolve reference to obtain correct identifier
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolver tokenResolver = tokenResolverFactory.createTokenResolver(tokenName);
final String identifier = tokenResolver.deResolve(elementMapping.getIdentifier(), reference, container);
if (target instanceof org.eclipse.emf.ecore.EObject) {
image = getImage((org.eclipse.emf.ecore.EObject) target);
}
boolean matchesPrefix = matches(identifier, prefix);
resultSet.add(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal(expectedTerminal, identifier, prefix, matchesPrefix, reference, container, image));
}
}
return resultSet;
}
return java.util.Collections.emptyList();
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleAttribute(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedStructuralFeature expectedFeature, org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EAttribute attribute, String prefix) {
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> resultSet = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>();
Object[] defaultValues = attributeValueProvider.getDefaultValues(attribute);
if (defaultValues != null) {
for (Object defaultValue : defaultValues) {
if (defaultValue != null) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolverFactory tokenResolverFactory = metaInformation.getTokenResolverFactory();
String tokenName = expectedFeature.getTokenName();
if (tokenName != null) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextTokenResolver tokenResolver = tokenResolverFactory.createTokenResolver(tokenName);
if (tokenResolver != null) {
String defaultValueAsString = tokenResolver.deResolve(defaultValue, attribute, container);
boolean matchesPrefix = matches(defaultValueAsString, prefix);
resultSet.add(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal(expectedTerminal, defaultValueAsString, prefix, matchesPrefix, expectedFeature.getFeature(), container));
}
}
}
}
}
return resultSet;
}
/**
* Creates a set of completion proposals from the given keyword.
*/
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleKeyword(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedCsString csString, String prefix) {
String proposal = csString.getValue();
boolean matchesPrefix = matches(proposal, prefix);
return java.util.Collections.singleton(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal(expectedTerminal, proposal, prefix, matchesPrefix, null, null));
}
/**
* Creates a set of (two) completion proposals from the given boolean terminal.
*/
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleBooleanTerminal(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedBooleanTerminal expectedBooleanTerminal, String prefix) {
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> result = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>(2);
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.grammar.MtextBooleanTerminal booleanTerminal = expectedBooleanTerminal.getBooleanTerminal();
result.addAll(handleLiteral(expectedTerminal, booleanTerminal.getAttribute(), prefix, booleanTerminal.getTrueLiteral()));
result.addAll(handleLiteral(expectedTerminal, booleanTerminal.getAttribute(), prefix, booleanTerminal.getFalseLiteral()));
return result;
}
/**
* Creates a set of completion proposals from the given enumeration terminal. For
* each enumeration literal one proposal is created.
*/
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleEnumerationTerminal(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedEnumerationTerminal expectedEnumerationTerminal, String prefix) {
java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> result = new java.util.LinkedHashSet<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal>(2);
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.grammar.MtextEnumerationTerminal enumerationTerminal = expectedEnumerationTerminal.getEnumerationTerminal();
java.util.Map<String, String> literalMapping = enumerationTerminal.getLiteralMapping();
for (String literalName : literalMapping.keySet()) {
result.addAll(handleLiteral(expectedTerminal, enumerationTerminal.getAttribute(), prefix, literalMapping.get(literalName)));
}
return result;
}
protected java.util.Collection<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal> handleLiteral(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal, org.eclipse.emf.ecore.EAttribute attribute, String prefix, String literal) {
if ("".equals(literal)) {
return java.util.Collections.emptySet();
}
boolean matchesPrefix = matches(literal, prefix);
return java.util.Collections.singleton(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.ui.MtextCompletionProposal(expectedTerminal, literal, prefix, matchesPrefix, null, null));
}
/**
* Calculates the prefix for each given expected element. The prefix depends on
* the current document content, the cursor position, and the position where the
* element is expected.
*/
protected void setPrefixes(java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedElements, String content, int cursorOffset) {
if (cursorOffset < 0) {
return;
}
for (org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedElement : expectedElements) {
String prefix = findPrefix(expectedElements, expectedElement, content, cursorOffset);
expectedElement.setPrefix(prefix);
}
}
public org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[] getElementsExpectedAt(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[] allExpectedElements, int cursorOffset) {
java.util.List<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal> expectedAtCursor = new java.util.ArrayList<org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal>();
for (int i = 0; i < allExpectedElements.length; i++) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedElement = allExpectedElements[i];
int startIncludingHidden = expectedElement.getStartIncludingHiddenTokens();
int end = getEnd(allExpectedElements, i);
if (cursorOffset >= startIncludingHidden && cursorOffset <= end) {
expectedAtCursor.add(expectedElement);
}
}
return expectedAtCursor.toArray(new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[expectedAtCursor.size()]);
}
/**
* Calculates the end index of the expected element at allExpectedElements[index].
* To determine the end, the subsequent expected elements from the array of all
* expected elements are used. An element is considered to end one character
* before the next elements starts.
*/
protected int getEnd(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal[] allExpectedElements, int indexInList) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtIndex = allExpectedElements[indexInList];
int startIncludingHidden = elementAtIndex.getStartIncludingHiddenTokens();
int startExcludingHidden = elementAtIndex.getStartExcludingHiddenTokens();
for (int i = indexInList + 1; i < allExpectedElements.length; i++) {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal elementAtI = allExpectedElements[i];
int startIncludingHiddenForI = elementAtI.getStartIncludingHiddenTokens();
int startExcludingHiddenForI = elementAtI.getStartExcludingHiddenTokens();
if (startIncludingHidden != startIncludingHiddenForI || startExcludingHidden != startExcludingHiddenForI) {
return startIncludingHiddenForI - 1;
}
}
return Integer.MAX_VALUE;
}
/**
* Checks whether the given proposed string matches the prefix. The two strings
* are compared ignoring the case. The prefix is also considered to match if is a
* camel case representation of the proposal.
*/
protected boolean matches(String proposal, String prefix) {
if (proposal == null || prefix == null) {
return false;
}
return (proposal.toLowerCase().startsWith(prefix.toLowerCase()) || org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextStringUtil.matchCamelCase(prefix, proposal) != null) && !proposal.equals(prefix);
}
protected org.eclipse.swt.graphics.Image getImage(org.eclipse.emf.ecore.EObject element) {
if (!org.eclipse.core.runtime.Platform.isRunning()) {
return null;
}
org.eclipse.emf.edit.provider.ComposedAdapterFactory adapterFactory = new org.eclipse.emf.edit.provider.ComposedAdapterFactory(org.eclipse.emf.edit.provider.ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
adapterFactory.addAdapterFactory(new org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory());
org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider labelProvider = new org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider(adapterFactory);
return labelProvider.getImage(element);
}
protected org.eclipse.emf.ecore.EObject findCorrectContainer(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextExpectedTerminal expectedTerminal) {
org.eclipse.emf.ecore.EObject container = expectedTerminal.getContainer();
org.eclipse.emf.ecore.EClass ruleMetaclass = expectedTerminal.getTerminal().getRuleMetaclass();
if (ruleMetaclass.isInstance(container)) {
// container is correct for expected terminal
return container;
}
// the container is wrong
org.eclipse.emf.ecore.EObject parent = null;
org.eclipse.emf.ecore.EObject previousParent = null;
org.eclipse.emf.ecore.EObject correctContainer = null;
org.eclipse.emf.ecore.EObject hookableParent = null;
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.grammar.MtextContainmentTrace containmentTrace = expectedTerminal.getContainmentTrace();
org.eclipse.emf.ecore.EClass startClass = containmentTrace.getStartClass();
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextContainedFeature currentLink = null;
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextContainedFeature previousLink = null;
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextContainedFeature[] containedFeatures = containmentTrace.getPath();
for (int i = 0; i < containedFeatures.length; i++) {
currentLink = containedFeatures[i];
if (i > 0) {
previousLink = containedFeatures[i - 1];
}
org.eclipse.emf.ecore.EClass containerClass = currentLink.getContainerClass();
hookableParent = findHookParent(container, startClass, currentLink, parent);
if (hookableParent != null) {
// we found the correct parent
break;
} else {
previousParent = parent;
parent = containerClass.getEPackage().getEFactoryInstance().create(containerClass);
if (parent != null) {
if (previousParent == null) {
// replace container for expectedTerminal with correctContainer
correctContainer = parent;
} else {
// This assignment is only performed to get rid of a warning about a potential
// null pointer access. Variable 'previousLink' cannot be null here, because it is
// initialized for all loop iterations where 'i' is greather than 0 and for the
// case where 'i' equals zero, this path is never executed, because
// 'previousParent' is null in this case.
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextContainedFeature link = previousLink;
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextEObjectUtil.setFeature(parent, link.getFeature(), previousParent, false);
}
}
}
}
if (correctContainer == null) {
correctContainer = container;
}
if (currentLink == null) {
return correctContainer;
}
hookableParent = findHookParent(container, startClass, currentLink, parent);
final org.eclipse.emf.ecore.EObject finalHookableParent = hookableParent;
final org.eclipse.emf.ecore.EStructuralFeature finalFeature = currentLink.getFeature();
final org.eclipse.emf.ecore.EObject finalParent = parent;
if (parent != null && hookableParent != null) {
expectedTerminal.setAttachmentCode(new Runnable() {
public void run() {
org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextEObjectUtil.setFeature(finalHookableParent, finalFeature, finalParent, false);
}
});
}
return correctContainer;
}
/**
* Walks up the containment hierarchy to find an EObject that is able to hold
* (contain) the given object.
*/
protected org.eclipse.emf.ecore.EObject findHookParent(org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EClass startClass, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp.MtextContainedFeature currentLink, org.eclipse.emf.ecore.EObject object) {
org.eclipse.emf.ecore.EClass containerClass = currentLink.getContainerClass();
while (container != null) {
if (containerClass.isInstance(object)) {
if (startClass.equals(container.eClass())) {
return container;
}
}
container = container.eContainer();
}
return null;
}
}